package com.hyper_yang.javase.streamAPI;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamBaseTest {
    @Test
    public void test01() {
        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list, 1, 8, 6, 9, 5, 3, 47);

//        int max = list.get(0);
//        for (int i : list) {
//            max = Math.max(max,i);
//        }
//        System.out.println(max);

        Optional<Integer> max = list.stream().max(Integer::compare);
        System.out.println(max);
    }

    @DisplayName("Stream创建操作: ")
    @Test     // 创建 stream: 通过一个数据源，获取一个流
    public void creatStream() {
/*
    创建 stream: 通过一个数据源（如: 集合、数组），获取一个流
        1、数组.     2、集合.     3、stream.静态方法
    中间操作: 每次处理都会返回一个持有结果的新 stream，即中间操作的返回值仍是 stream类型的对象，
        因此中间操作可以是个操作链，可对数据源的数据进行 n次处理，但是在终结操作前，并不会真正执行。
    终止操作: 终止操作的方法返回值不再是 stream，因此一旦执行终止操作，就结束整个 stream操作了
        一旦执行终止操作，就执行中间操作链，最终产生结果并结束 stream.
 */
        int[] arr = {4, 3, 5, 2, 1};
        // 原材料 创建流    处理
        Arrays.stream(arr).forEach(System.out::println);

        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list, 1, 8, 6, 9, 5, 3, 47);
        // 串行流: 必须按照流程依次处理，全程只有一个工人。
        list.stream().forEach(System.out::println);
        // 并行流: 不保证输出顺序，全程右多个工人，分别在输出。
        list.parallelStream().forEach(System.out::println);
        // 有限流: 指定了多少元素，则流中有多少元素。
        Stream.of(1, 8, 6, 9, 5, 3, 47).sorted().forEach(System.out::println);
        // 无限流: 调用一个供给型接口，调用一次，流中就多一个数据。
        Stream.generate(Math::random).limit(10).forEach(System.out::println);
    }

    @DisplayName("Stream处理操作: ")
    @Test    // 中间操作: 对原材料、数据源做加工处理
    public void handleStream() {
/*
    自身不存储数据：
        1、处理 1完成，交给处理2;
        2、处理完成交给终止操作。 【没有终止操作就不会执行】
    常用操作:
        Stream filter(Perdicate p)
        Stream distinct()
        Stream limit(Long maxSize)
        Stream skip(long n)
        Stream peek(Consumer action)
        Stream sorted()
        Stream sorted(Comparator compare)
        Stream map(Function f)
        Stream mapToDouble(ToDoubleFunction f)
        Stream mapToInt(ToIntFunction f)
        Stream mapToLong(ToLongFunction f)
        Stream flatMap(Function f)
 */
        ArrayList<Integer> numList = new ArrayList<>();
        Collections.addAll(numList, 4, 3, 25, 15, 2, 62, 9, 84, 7, 2);
        // 对数据做过滤只保留偶数
        numList.stream()    // 创建流
                .skip(2)    // 跳过指定数量的数据
                .peek(i -> System.out.println("peek1: " + i + "\t"))
                .filter(i -> i % 2 == 0)    // 按照指定规则过滤
                .peek(i -> System.out.println("peek2: " + i + "\t"))    //
                .distinct()    // 去除重复元素
                .limit(3)    // 限制输出个数
                .forEach(i -> System.out.println("forEach: " + i + "\t"));    // 打印输出结果

        // 使用存储的数据的类型内部比较器
//        numList.stream().sorted().forEach(System.out::print);
        numList.stream().sorted((o1, o2) -> o2 - o1).forEach(i -> System.out.print(i + "\t"));

        // 在流中，将数据映射为另一个结果
        numList.stream().map(i -> i + 1).forEach(System.out::print);
        numList.stream().mapToDouble(i -> i).forEach(System.out::println);

        // flatMap: 接收一个函数作为参数，将流中的每个值都换成另一个流，然后把所有流连接成一个流
        ArrayList<String> people = new ArrayList<>();
        Collections.addAll(people, "小梅子", "小廖子", "小钟子");
        people.stream().flatMap(new Function<String, Stream<?>>() {
            @Override
            public Stream<?> apply(String s) {
                String[] split = s.split("");
                Stream<String> stream = Arrays.stream(split);
                return stream;
            }
        }).forEach(System.out::println);
    }

    @DisplayName("Stream终止操作: ")
    @Test    // 终止操作: 必须要有，否则中间操作不执行
    public void endStream() {
/*
常见操作:
    allMatch(Predicate p)
    anyMatch(Predicate p)
    noneMatch(Predicate p)
    findFirst()
    findAny()
    count()
    max(Comparator c)
    min(Comparator c)
    forEach(Consumer c)
    reduce(T iden, BinaryOperator b)
    reduce(BinaryOperator b)
    collect(Collector c)
 */
        // todo: 匹配
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "菜菜", "徐徐", "坤坤", "ikun");

//        boolean b = list.stream().allMatch(s -> s.length() >= 3); // false
//        boolean b = list.stream().anyMatch(s -> s.length() >= 3); // true
        boolean b = list.stream().noneMatch(s -> s.length() >= 3); // false

        Optional<String> first = list.stream().filter(s -> s.length() == 2).findFirst();
        System.out.println(first); // 找结果中第一个
        Optional<String> any = list.parallelStream().filter(s -> s.length() == 2).findAny();
        System.out.println(any); // 找结果中任意一个
        long count = list.stream().filter(s -> s.length() == 2).count();
        System.out.println(count); // 计算结果的个数
        Optional<String> max = list.stream().max(String::compareTo);
        System.out.println(max); // 结果中的最大值
        Optional<String> min = list.stream().min(String::compareTo);
        System.out.println(min); // 结果中的最小值

        // todo: 随机生成 10个随机数，累加求和
        Random random = new Random();
        Optional<Integer> reduce = Stream.generate(() -> random.nextInt(50) + 1)
                .peek(System.out::println)
                .limit(10)
//                .reduce(new BinaryOperator<Integer>() {
//                    @Override
//                    public Integer apply(Integer i, Integer i2) {
////                        return i + i2;
//                        return Integer.sum(i,i2);
//                    }
//                });
                .reduce(Integer::sum);
        // 使用关联累计函数对此流的元素执行缩减，并返回描述 Optional缩减值(如果有)的值，但不受顺序执行限制
        System.out.println("reduce: " + reduce.get());

        // todo: reduce(T iden, BinaryOperator b)
        // identity: var == 0 -> 代表结果不再是 Optional而是 Integer
        // var > 0 -> 基本值，累加生成的值
        Integer reduce1 = Stream.generate(() -> random.nextInt(50) + 1)
                .peek(System.out::println)
                .limit(10)
                .reduce(0, Integer::sum); // 可以将流中的元素反复结合起来，得到一个值，返回 T

        System.out.println("reduce1: " + reduce1);

        // todo: 将 list集合中名字长度 == 2的元素取出来，存在一个新集合里。
        List<String> collect = list.stream()
                .filter(s -> s.length() == 2)
//                .collect(Collectors.toList());
                .toList();

        collect.forEach(System.out::println);
    }
}
